/*
 * Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.com
 * The software in this package is published under the terms of the CPAL v1.0
 * license, a copy of which has been included with this distribution in the
 * LICENSE.txt file.
 */
package org.mule.runtime.core.util.counters.impl;

import org.mule.runtime.core.util.counters.CounterFactory.Type;

import java.security.InvalidParameterException;
import java.util.Iterator;
import java.util.LinkedList;

public class RatePerUnit extends AggregateCounter {

  private static class Sample {

    private double value;
    private long time;

    public Sample(double value, long time) {
      this.value = value;
      this.time = time;
    }

    /**
     * @return the time of the sample
     */
    public long getTime() {
      return time;
    }

    /**
     * @return the value of the sample
     */
    public double getValue() {
      return value;
    }
  }

  private final LinkedList<Sample> samples;
  private final long unit;
  private final long length;
  private final long baseTime;

  public RatePerUnit(String name, String p, Type type, AbstractCounter base) {
    super(name, type, base);

    if (type == Type.RATE_PER_SECOND) {
      unit = 1000;
    } else if (type == Type.RATE_PER_MINUTE) {
      unit = 60 * 1000;
    } else if (type == Type.RATE_PER_HOUR) {
      unit = 60 * 60 * 1000;
    } else {
      throw new InvalidParameterException();
    }

    long newLength = 0;

    try {
      newLength = Long.parseLong(p);
    } catch (Exception e) {
      newLength = 0;
    } finally {
      if (newLength <= 0) {
        newLength = 128;
      }

      length = newLength;
    }

    samples = new LinkedList<Sample>();
    baseTime = System.currentTimeMillis();
  }

  @Override
  public double nextValue() {
    if (samples.isEmpty()) {
      return 0.0;
    } else {
      double total = 0.0;
      long current = getTime();
      Iterator<Sample> it = samples.iterator();
      Sample sample = null;
      while (it.hasNext()) {
        sample = it.next();
        if (current - sample.time > length) {
          break;
        }
        total += sample.value;
      }
      return total / (1 + current - (sample != null ? sample.time : 0));
    }
  }

  @Override
  public void doCompute() {
    Sample l = samples.isEmpty() ? null : (Sample) samples.getFirst();
    long t = getTime();
    if (l == null || t > l.time) {
      Sample s = new Sample(this.getBase().nextValue(), t);
      samples.addFirst(s);
    } else {
      l.value += getBase().nextValue();
    }
    while (samples.size() > length) {
      samples.removeLast();
    }
  }

  protected long getTime() {
    return (System.currentTimeMillis() - baseTime) / unit;
  }
}
